/*
 * Copyright (c) 2011-2023, ThinkWide (cnet2001@163.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thinkwide.data.ormapping;

import com.thinkwide.data.core.MFKeyValue;
import com.thinkwide.data.core.MFString;
import com.thinkwide.data.ormapping.datatable.MFDataColumnException;
import com.thinkwide.data.ormapping.datatable.MFDataRow;
import com.thinkwide.data.ormapping.datatable.MFDataTable;
import com.thinkwide.data.ormapping.dialect.MFOperateCrossSql;
import com.thinkwide.data.ormapping.page.MFPage;
import com.thinkwide.data.ormapping.sql.MFSqlBase;
import org.apache.commons.collections4.map.LinkedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * MFQuery条件查询泛型基类
 *
 * @author AndyZhang 2011 cnet2001@163.com
 */
public class MFQuery {
    private static Logger logger = LoggerFactory.getLogger(MFQuery.class);


    public static MFDataAdapter getAdapter(MFDataAdapter dataAdapter) {
        return new MFDataAdapter();
    }

    /**
     * 对象化分页查询数据
     *
     * @param classType
     * @param beginIndex
     * @param endIndex
     * @param sql
     * @param <T>
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static <T> MFPage<T> findPageBySql(Class<T> classType, int beginIndex, int endIndex, String sql) {
        return findPageBySql(null, classType, beginIndex, endIndex, sql);
    }

    public static <T> MFPage<T> findPageBySql(MFDataAdapter dataAdapter, Class<T> classType, int beginIndex, int endIndex, String sql) {
        MFPage<T> page = MFActiveRecordBase.findPageBySql(dataAdapter, classType, beginIndex, endIndex, sql);

        return page;
    }

    /**
     * 对象化查询数据
     *
     * @param classType
     * @param sql
     * @param <T>
     * @return
     */
    public static <T> List<T> findListBySql(Class<T> classType, String sql) {
        return findListBySql(null, classType, sql);
    }

    public static <T> List<T> findListBySql(Class<T> classType, String sql, Object... cmdParams) {
        return findListBySql(null, classType, sql, cmdParams);
        //return findObjAll(null, classType, MFSqlBase.getStaticSql(sql, args));
    }

    public static <T> List<T> findListBySql(MFDataAdapter dataAdapter, Class<T> classType, String sql) {
        List<T> list = MFActiveRecordBase.findListBySql(dataAdapter, classType, sql);
        return list;
    }

    public static <T> List<T> findListBySql(MFDataAdapter dataAdapter, Class<T> classType, String sql, Object... cmdParams) {
        List<T> list = MFActiveRecordBase.findListBySql(dataAdapter, classType, sql, cmdParams);
        return list;
    }


    /**
     * 对象化查询单条数据
     *
     * @param classType
     * @param sql
     * @param args
     * @param <T>
     * @return
     */
    public static <T> T findOneBySql(Class<T> classType, String sql, Object... args) {
        return MFActiveRecordBase.findOneBySql(classType, sql, args);
    }

    public static <T> T findOneBySql(MFDataAdapter dataAdapter, Class<T> classType, String sql, Object... args) {
        return MFActiveRecordBase.findOneBySql(dataAdapter, classType, sql, args);
    }

    public static MFDataTable findDataBySql(String sql) {
        return findDataBySql(null, sql);
    }

    public static MFDataRow findDataRow(String sql, Object... args) {
        return findDataRow(null, sql, args);
    }

    public static MFDataRow findDataRow(MFDataAdapter dataAdapter, String sql, Object... args) {
        MFDataTable table = findDataBySql(dataAdapter, sql, args);
        if (table.isEmpty()) {
            return null;
        } else {
            return table.getRows().get(0);
        }
    }

    public static MFDataRow findDataRow(MFDataAdapter dataAdapter, String sql) {
        MFDataTable table = findDataBySql(dataAdapter, sql);
        if (table.isEmpty()) {
            return null;
        } else {
            return table.getRows().get(0);
        }
    }

    public static MFDataRow findDataRow(String sql) {
        return findDataRow(null, sql);
    }

    public static int deleteByKey(String tableName, String key, String keyValue) throws SQLException {
        return deleteByKey(null, tableName, key, keyValue);
    }

    public static int deleteByKey(MFDataAdapter dataAdapter, String tableName, String key, String keyValue) throws SQLException {
        String sql = String.format("delete  from %s where %s=%s", tableName, key, "?");
        if (dataAdapter == null) {
            MFDataAdapter da = new MFDataAdapter();
            int i = da.execSql(sql, keyValue);
            da.free();
            da = null;
            return i;
        } else {
            return dataAdapter.execSql(sql, keyValue);
        }

        //return MFquery.execSql(dataAdapter,String.format("delete  from %s where %s='%s'",tableName,key,keyValue));
    }

    public static int deleteByKeys(String tableName, String key, String keyValue) {
        return deleteByKeys(null, tableName, key, keyValue);
    }

    public static int deleteByKeys(MFDataAdapter dataAdapter, String tableName, String key, String keyValue) {
        String[] str = keyValue.split(",");
        String sql = "";
        for (String item : str) {
            if (MFString.notNullOrEmpty(sql)) {
                sql = sql + String.format(",'%s'", item);
            } else {
                sql = sql + String.format("'%s'", item);
            }
        }

        return MFQuery.execSql(dataAdapter,
                String.format("delete from %s where %s in (%s)", tableName, key, sql));
    }

    public static MFDataTable findByKey(String tableName, String key, String keyValue) throws SQLException, MFDataColumnException {
        return findByKey(null, tableName, key, keyValue);
    }

    public static MFDataTable findByKey(MFDataAdapter dataAdapter, String tableName, String key, String keyValue) throws SQLException, MFDataColumnException {
        String sql = String.format("select * from %s where %s=%s", tableName, key, "?");

        if (dataAdapter == null) {
            MFDataAdapter da = new MFDataAdapter();
            MFDataTable dt = da.getDataTable(sql, keyValue);
            dt.setTableName(tableName);
            dt.setPrimaryFields(key);
            da.free();
            da = null;
            return dt;
        } else {
            MFDataTable dt = dataAdapter.getDataTable(sql, keyValue);
            dt.setTableName(tableName);
            return dt;
        }

        //return MFQuery.findDataBySql(dataAdapter,String.format("select * from %s where %s='%s'",tableName,key,keyValue));
    }


    public static MFDataTable findDataBySql(String sql, Object... args) {
        return findDataBySql(null, sql, args);
    }

    public static MFDataTable findDataBySql(MFDataAdapter dataAdapter, String sql, Object... args) {
        sql = MFSqlBase.getStaticSql(sql, args);
        return findDataBySql(dataAdapter, sql);
    }

    public static MFDataTable findDataBySql(MFDataAdapter dataAdapter, String sql) {
        MFDataTable dt = null;

        try {
            if (dataAdapter == null) {
                MFDataAdapter da = new MFDataAdapter();
                dt = da.getDataTable(sql);
                da.free();
                da = null;
            } else {
                dt = dataAdapter.getDataTable(sql);
            }
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return dt;
    }

    public static Object execScalar(String sql, Object... cmdParams) throws SQLException {
        return execScalar(null, sql, cmdParams);
    }

    public static Object execScalar(String sql) throws SQLException {
        return execScalar(null, sql);
    }

    public static Object execScalar(MFDataAdapter dataAdapter, String sql) throws SQLException {
        Object obj = null;

        try {
            if (dataAdapter == null) {
                MFDataAdapter da = new MFDataAdapter();
                obj = da.execScalar(sql);
                da.free();
                da = null;
            } else {
                obj = dataAdapter.execScalar(sql);
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return obj;
    }

    public static Object execScalar(MFDataAdapter dataAdapter, String sql, Object... cmdParams) throws SQLException {
        Object obj = null;

        try {
            if (dataAdapter == null) {
                MFDataAdapter da = new MFDataAdapter();
                obj = da.execScalar(sql, cmdParams);
                da.free();
                da = null;
            } else {
                obj = dataAdapter.execScalar(sql, cmdParams);
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return obj;
    }


    public static int execSql(String sql) {
        return execSql(null, sql);
    }

    public static int getCountBySql(String sql) throws SQLException {
        return getCountBySql(null, sql);
    }

    public static int getCountBySql(MFDataAdapter dataAdapter, String sql) throws SQLException {
        Object obj = execScalar(dataAdapter, String.format("Select Count(*) from (%s) a ", sql));
        if (obj instanceof BigDecimal) {
            BigDecimal i = (BigDecimal) obj;
            return i.intValue();
        } else if (obj instanceof Integer) {
            return (Integer) obj;
        }
        if (obj instanceof BigInteger) {
            return ((BigInteger) obj).intValue();
        }
        return -1;
    }

    public static int execSql(MFDataAdapter dataAdapter, String sql) {
        int iCount = -1;
        try {
            if (dataAdapter == null) {
                MFDataAdapter da = new MFDataAdapter();
                iCount = da.execSql(sql);
                da.free();
                da = null;
            } else {
                iCount = dataAdapter.execSql(sql);
            }

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return iCount;
    }

    public static int execSql(String sql, Object... cmdParams) {
        return execSql(null, sql, cmdParams);
    }

    public static int execSql(MFDataAdapter dataAdapter, String sql, Object... cmdParams) {
        int iCount = -1;

        try {

            if (dataAdapter == null) {
                MFDataAdapter da = new MFDataAdapter();
                iCount = da.execSql(sql, cmdParams);
                da.free();
                da = null;
            } else {
                iCount = dataAdapter.execSql(sql, cmdParams);
            }


        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return iCount;
    }

    public static MFDataTable findPageDataBySql(String sql, int pageIndex, int pageSize) {
        return findPageDataBySql(null, sql, pageIndex, pageSize);
    }

    public static MFDataTable findPageDataBySql(String sql, int pageIndex, int pageSize, String orderBy) {
        return findPageDataBySql(null, sql, pageIndex, pageSize, orderBy);
    }

    public static MFDataTable findPageDataBySql(MFDataAdapter dataAdapter, String sql, int pageIndex, int pageSize, String orderBy) {
        MFDataTable dt = null;

        try {

            if (dataAdapter == null) {
                MFDataAdapter da = new MFDataAdapter();

                MFOperateCrossSql CrossSQL = new MFOperateCrossSql(da);
                sql = CrossSQL.getSelectPageSqlExt(sql, "", orderBy, pageSize * (pageIndex - 1), pageIndex * pageSize);

                dt = da.getDataTable(sql);
                dt.getPages().calculateByIndex(pageSize * (pageIndex - 1), pageIndex * pageSize, da.getCount(sql));
                da.free();
                da = null;
            } else {
                MFOperateCrossSql CrossSQL = new MFOperateCrossSql(dataAdapter);
                sql = CrossSQL.getSelectPageSqlExt(sql, "", orderBy, pageSize * (pageIndex - 1), pageIndex * pageSize);

                dt = dataAdapter.getDataTable(sql);
                dt.getPages().calculateByIndex(pageSize * (pageIndex - 1), pageIndex * pageSize, dataAdapter.getCount(sql));
            }
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return dt;
    }

    public static MFDataTable findPageDataBySql(MFDataAdapter dataAdapter, String sql, int pageIndex, int pageSize) {
        MFDataTable dt = null;

        try {

            if (dataAdapter == null) {
                MFDataAdapter da = new MFDataAdapter();

                MFOperateCrossSql CrossSQL = new MFOperateCrossSql(da);
                sql = CrossSQL.getSelectPageSql(sql, pageSize * (pageIndex - 1), pageIndex * pageSize);

                dt = da.getDataTable(sql);
                dt.getPages().calculateByIndex(pageSize * (pageIndex - 1), pageIndex * pageSize, da.getCount(sql));
                da.free();
                da = null;
            } else {
                MFOperateCrossSql CrossSQL = new MFOperateCrossSql(dataAdapter);
                sql = CrossSQL.getSelectPageSql(sql, pageSize * (pageIndex - 1), pageIndex * pageSize);

                dt = dataAdapter.getDataTable(sql);
                dt.getPages().calculateByIndex(pageSize * (pageIndex - 1), pageIndex * pageSize, dataAdapter.getCount(sql));
            }
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return dt;
    }


    public static ResultSet findResultSet(MFDataAdapter dataAdapter, String sql) {
        ResultSet rs = null;

        try {

            rs = dataAdapter.getResultSet(sql);
            return rs;
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return rs;
    }


    public static ResultSet findPageResultSet(MFDataAdapter dataAdapter, String sql, int beginIndex, int endIndex) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ResultSet rs = null;
        MFOperateCrossSql CrossSQL = new MFOperateCrossSql(dataAdapter);
        sql = CrossSQL.getSelectPageSql(sql, beginIndex, endIndex);
        try {
            rs = dataAdapter.getResultSet(sql);
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
        return rs;
    }

    public static MFKeyValue findKeyValue(MFDataAdapter dataAdapter, String sql) {
        MFDataRow row = findDataRow(dataAdapter, sql);
        MFKeyValue kv = null;
        if (row != null) {
            kv = new MFKeyValue(row.getString(0), row.getString(1));
        }
        return kv;
    }


    public static MFKeyValue findKeyValue(String sql) {
        return findKeyValue(null, sql);
    }

    public static List<MFKeyValue> findKeyValueList(MFDataAdapter dataAdapter, String sql, int pageIndex, int pageSize, String orderBy) {
        List<MFKeyValue> list = new ArrayList<MFKeyValue>();
        MFDataTable table = findPageDataBySql(dataAdapter, sql, pageIndex, pageSize, orderBy);
        if (table != null) {
            for (MFDataRow row : table.getRows()) {
                MFKeyValue kv = new MFKeyValue(row.getString(0), row.getString(1));
                list.add(kv);
            }
        }
        return list;
    }

    public static List<MFKeyValue> findKeyValueList(MFDataAdapter dataAdapter, String sql) {
        List<MFKeyValue> list = new ArrayList<MFKeyValue>();
        MFDataTable table = findDataBySql(dataAdapter, sql);
        if (table != null) {
            for (MFDataRow row : table.getRows()) {
                MFKeyValue kv = new MFKeyValue(row.getString(0), row.getString(1));
                list.add(kv);
            }
        }

        return list;
    }

    public static List<MFKeyValue> findKeyValueList(String sql) {
        return findKeyValueList(null, sql);
    }

    public static Map findMap(MFDataAdapter dataAdapter, String sql) {
        Map map = new LinkedMap();
        MFDataTable table = findDataBySql(dataAdapter, sql);
        if (table != null) {
            for (MFDataRow row : table.getRows()) {
                map.put(row.getString(0), row.getString(1));
            }
        }

        return map;
    }

    public static Map findMap(String sql) {
        return findMap(null, sql);
    }


    public static int deleteObj(MFDataAdapter dataAdapter, Object obj) throws Throwable {
        if (Objects.isNull(dataAdapter)) {
            MFDataAdapter da = new MFDataAdapter();
            int count = da.delete(obj);
            da.free();
            da = null;
            return count;
        } else {
            return dataAdapter.delete(obj);
        }
    }

    public static int deleteObj(Object obj) throws Throwable {
        return deleteObj(null, obj);
    }

    public static int deleteObjList(MFDataAdapter dataAdapter, List list) {
        int count = 0;
        for (Object obj : list) {
            try {
                count = count + deleteObj(dataAdapter, obj);
            } catch (Throwable e) {
                logger.error(e.getMessage(), e);
            }
        }
        return count;
    }

    public static int insertObj(MFDataAdapter dataAdapter, Object obj) throws Throwable {

        if (Objects.isNull(dataAdapter)) {
            MFDataAdapter da = new MFDataAdapter();
            int count = da.insert(obj);
            da.free();
            da = null;
            return count;
        } else {
            return dataAdapter.insert(obj);
        }
    }

    public static int insertObj(Object obj) throws Throwable {
        return insertObj(obj);
    }

    public static int updateObj(MFDataAdapter dataAdapter, Object obj) throws Throwable {
        if (Objects.isNull(dataAdapter)) {
            MFDataAdapter da = new MFDataAdapter();
            int count = da.update(obj);
            da.free();
            da = null;
            return count;
        } else {
            return dataAdapter.update(obj);
        }
    }

    /**
     * 更新对象
     *
     * @param obj
     * @return
     * @throws Throwable
     */
    public static int updateObj(Object obj) throws Throwable {
        return updateObj(obj);
    }


    public static <T> int getCountObj(MFDataAdapter dataAdapter, Class<T> classType, String where, Object... args) throws SQLException {
        if (Objects.isNull(dataAdapter)) {
            MFDataAdapter da = new MFDataAdapter();
            int count = da.getCount(classType, where, args);
            da.free();
            da = null;
            return count;
        } else {
            return dataAdapter.getCount(classType, where, args);
        }
    }

    public static <T> int getCountObj(Class<T> classType, String where, Object... args) throws SQLException {
        return getCountObj(null, classType, where, args);
    }

}
